import fso from "@/utils/FileOperate/operate";
import sm from "@/utils/ScheduleManage/operate";
import handleWSCmd, { type HandleWSCmdParams } from "@/utils/websocket/handleWSCmd";
import WebSocketOperator from "@anlib/websocket-operator";
import config from "@/config";
import store from "@/store";
import { setIsWebsocketConnect } from "@/store/slices/index";

let wsOperator: WebSocketOperator | null = null;

/** 修改 redux 里的 isWebsocketConnect 的状态 */
const setWsConnect = (flag: boolean) => {
  if (store.getState().index.isWebsocketConnect !== flag) {
    if (config.IS_DEBUG) {
      sm.log("修改 websocket 连接状态为: ", flag ? "已连接" : "未连接");
    }
    store.dispatch(setIsWebsocketConnect(flag));
  }
};

/** 连接websocket */
const connect = () => {
  return new Promise<null | WebSocketOperator>(resolve => {
    // 判断是否重复连接
    const storeRedux = store.getState();
    if (storeRedux.index.isWebsocketConnect && wsOperator) {
      fso.info("当前已经连接上websocke");
      resolve(wsOperator);
      return;
    }

    if (!storeRedux.index.entryNetworkInfo?.code) {
      fso.info("没有获取设备编号, 不连接websocket");
      resolve(null);
      return;
    }

    if (wsOperator) wsOperator.destroy();

    // 是否兼容 websocket
    WebSocketOperator.isCompatibleWebSocket()
      .then(() => {
        const wsOpt = config.getWebsocketOpt();
        fso.info(`WebSocket 初始化: `, wsOpt);
        wsOperator = new WebSocketOperator(wsOpt);

        // 发送心跳时触发
        wsOperator.onheartbeat = params => {
          // TODO 会导致多次发送的问题
          if (config.IS_DEBUG) {
            sm.log(`WebSocket 发送第 ${params.heartbeatNum} 次心跳`);
          }
        };

        // 重新连接时触发
        wsOperator.onreconnection = params => {
          setWsConnect(false);
          fso.warn(`WebSocket 第${params.reconnectionNum}次重新连接: ${params.ws.url}`);
        };

        // 错误重试达到最大时触发
        wsOperator.onmaxReconnection = params => {
          setWsConnect(false);
          fso.error("WebSocket 错误重试达到最大 -> ", params.reconnectionNum.toString());
        };

        // 销毁时触发
        wsOperator.ondestroy = params => {
          setWsConnect(false);
          fso.error("销毁 WebSocket", params);
          resolve(null);
        };

        // 原本 WebSocket 的事件
        wsOperator.onmessage = params => {
          const e = params.event as WebSocketMessageEvent;
          fso.info("WebSocket 收到消息: ", e.data);
          try {
            const msg: HandleWSCmdParams = JSON.parse(e.data);
            if (msg) {
              handleWSCmd(msg);
            } else {
              fso.warn("WebSocket 消息为空", e.data);
            }
          } catch (err) {
            fso.error("WebSocket JSON 序列化失败 -> ", err);
          }
        };

        wsOperator.onopen = params => {
          setWsConnect(true);
          fso.info("WebSocket 连接成功");
          resolve(wsOperator);
        };

        wsOperator.onclose = params => {
          setWsConnect(false);
          fso.info("WebSocket 连接关闭: ", params);
          resolve(null);
        };

        wsOperator.onerror = params => {
          setWsConnect(false);
          const e = params.event as WebSocketErrorEvent;
          fso.error("WebSocket 连接错误", e.message);
          resolve(null);
        };
      })
      .catch(err => {
        console.log("err: ", err);
        setWsConnect(false);
        fso.error("WebSocket 连接发生了错误", err.message);
        resolve(null);
      });
  });
};

/** 注销 */
const destroy = () => {
  if (wsOperator) {
    wsOperator.destroy();
  } else {
    fso.warn(`WebSocket 未初始化, 不调用注销方法`);
  }
};

/** 发送数据 */
const sendData = (text: string) => {
  return new Promise<null | Error>(async resolve => {
    if (wsOperator) {
      wsOperator.send(text);
      fso.info(`WebSocket 发送数据: ${text}`);
      resolve(null);
    } else {
      fso.warn(`WebSocket 还未初始化, 重新连接: ${text}`);
      const newWs = await connect();
      if (newWs) {
        newWs.send(text);
        fso.info(`WebSocket 发送数据: ${text}`);
        resolve(null);
      } else {
        resolve(new Error("websocket 未连接成功"));
      }
    }
  });
};

export { wsOperator, connect, destroy, sendData, setWsConnect };
